﻿using HPSocketCS;
using Milink.Core;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace Milink.SaiKe.Remote
{
    public partial class FormRemote : Form
    {
        private HttpServer httpServer;
        private TcpServer tcpServer;
        private bool tcpIsOpen = false;
        private bool httpIsOpen = false;

        private Dictionary<string, byte[]> deviceFrame = new Dictionary<string, byte[]>();
        private Dictionary<string, byte> deviceVerNo = new Dictionary<string, byte>();

        private delegate void ShowMsg(string msg, string action);
        private ShowMsg ShowMsgDelegate;
        private int msgMaxWidth;

        #region showMsg
        private void showMsg(string msg, string action)
        {
            if (lbxMsg.InvokeRequired)
            {
                lbxMsg.Invoke(ShowMsgDelegate, msg, action);
            }
            else
            {
                try
                {
                    if (!chkDebug.Checked && action.IndexOf("debug") > -1) return;
                    LogHelper.info(string.Format("{0}\r\n{1}", action, msg));
                    if (!chkLite.Checked || action == "data" || action == "http request" || action.IndexOf("error") > -1)
                    {
                        while (lbxMsg.Items.Count > 1000)
                        {
                            lbxMsg.Items.RemoveAt(0);
                        }
                        lbxMsg.Items.Add(" ~  " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff") + " ~  " + action);
                        lbxMsg.Items.AddRange(msg.Split(new string[] { Environment.NewLine }, StringSplitOptions.None));
                        lbxMsg.TopIndex = lbxMsg.Items.Count - (lbxMsg.Height / lbxMsg.ItemHeight) + 3;
                        msgMaxWidth = 0;
                        foreach (string item in lbxMsg.Items)
                        {
                            msgMaxWidth = Math.Max(msgMaxWidth, (int)lbxMsg.CreateGraphics().MeasureString(item, lbxMsg.Font).Width);
                        }
                        lbxMsg.HorizontalExtent = msgMaxWidth + 10;
                    }
                }
                catch (Exception ex)
                {
                    lbxMsg.Items.Clear();
                    LogHelper.error("showMsg Error", ex);
                }
            }
        }
        #endregion

        #region setButton
        private void setButton(bool tcpIsOpen, bool httpIsOpen)
        {
            this.tcpIsOpen = tcpIsOpen;

            txtTcpPort.Enabled = !this.tcpIsOpen;       //初始状态
            btnStartTcp.Enabled = !this.tcpIsOpen;      //初始状态
            btnStopTcp.Enabled = this.tcpIsOpen;

            this.httpIsOpen = httpIsOpen;
            txtHttpPort.Enabled = !this.httpIsOpen;     //初始状态
            btnStartHttp.Enabled = !this.httpIsOpen;    //初始状态
            btnStopHttp.Enabled = this.httpIsOpen;
        }
        #endregion

        public FormRemote()
        {
            InitializeComponent();
            ShowMsgDelegate = new ShowMsg(showMsg);
            LogHelper.init(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
            txtTcpPort.Text = ConfigurationManager.AppSettings["TcpPort"] ?? "14444";
            txtHttpPort.Text = ConfigurationManager.AppSettings["HttpPort"] ?? "24444";
            txtAppUrl.Text = ConfigurationManager.AppSettings["AppUrl"];
            setButton(false, false);
        }

        #region FormRemote_FormClosing
        private void FormRemote_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                tcpServer.Destroy();
                httpServer.Destroy();
            }
            catch
            {

            }
        }
        #endregion 

        #region lbxMsg_DrawItem
        private void lbxMsg_DrawItem(object sender, DrawItemEventArgs e)
        {
            if (e.Index >= 0)
            {
                ListBox listBox = sender as ListBox;
                e.DrawBackground();
                Brush mybsh = Brushes.Black;
                if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
                {
                    mybsh = Brushes.White;
                }
                else if (listBox.Items[e.Index].ToString().StartsWith(" ~  "))
                {
                    mybsh = Brushes.Blue;
                }
                e.DrawFocusRectangle();
                e.Graphics.DrawString(listBox.Items[e.Index].ToString(), e.Font, mybsh, e.Bounds, StringFormat.GenericDefault);
            }
        }
        #endregion

        #region Http_OnBody
        private HttpParseResult Http_OnBody(IntPtr connectId, byte[] bytes)
        {
            string postBody = httpServer.GetExtra<string>(connectId);
            if (string.IsNullOrWhiteSpace(postBody)) postBody = "";
            postBody += Encoding.GetEncoding("gb2312").GetString(bytes).TrimEnd('\0').Replace('\0', '-');
            showMsg("body:" + bytes.Length + ":" + postBody, "debug http request body");
            httpServer.SetExtra(connectId, postBody);
            return HttpParseResult.Ok;
        }
        #endregion

        #region Http_OnMessageComplete
        private HttpParseResult Http_OnMessageComplete(IntPtr connectId)
        {
            AjaxResult result = new AjaxResult()
            {
                success = true,
                message = "ok"
            };
            try
            {
                string path = httpServer.GetUrlField(connectId, HttpUrlField.Path);
                if (path.ToLower() == "/saike")
                {
                    string queryString = httpServer.GetUrlField(connectId, HttpUrlField.QueryString);
                    Dictionary<string, string> requestParam = SaiKeUtility.getQueryParam(queryString);

                    string postBody = httpServer.GetExtra<string>(connectId);
                    if (!string.IsNullOrWhiteSpace(postBody))
                    {
                        requestParam.leftMerge(SaiKeUtility.getQueryParam(postBody));
                    }

                    showMsg(requestParam.serialize(), "debug http request");

                    //判断method
                    string request_method = requestParam.ContainsKey("method") ? requestParam["method"] : "";
                    if (string.IsNullOrWhiteSpace(request_method))
                    {
                        result = new AjaxResult(false, "miss method");
                    }
                    else
                    {
                        //判断deviceId
                        string request_deviceId = requestParam.ContainsKey("deviceId") ? requestParam["deviceId"] : "";
                        if (string.IsNullOrWhiteSpace(request_deviceId))
                        {
                            result = new AjaxResult(false, "miss deviceId");
                        }
                        else
                        {
                            //showMsg(string.Format("> {0}:{1},{2} method:{3}", httpServer.IpAddress, httpServer.Port, connectId, method), "http request");
                            //其它参数
                            string request_host_str = requestParam.ContainsKey("host") ? requestParam["host"] : "";
                            string request_loop_str = requestParam.ContainsKey("loop") ? requestParam["loop"] : "";
                            string request_startAddr_str = requestParam.ContainsKey("startAddr") ? requestParam["startAddr"] : "";
                            string request_endAddr_str = requestParam.ContainsKey("endAddr") ? requestParam["endAddr"] : "";
                            Byte.TryParse(request_host_str, out byte request_host);
                            Byte.TryParse(request_loop_str, out byte request_loop);
                            Byte.TryParse(request_startAddr_str, out byte request_startAddr);
                            Byte.TryParse(request_endAddr_str, out byte request_endAddr);
                            string request_param = requestParam.ContainsKey("param") ? requestParam["param"] : "";
                            bool cmdResult = false;
                            switch (request_method.ToLower())
                            {
                                case "test10k":
                                    SaiKe71 param71Test10k = new SaiKe71
                                    {
                                        host = 0,
                                        type = 0,
                                        start = 0,
                                        end = 80
                                    };
                                    cmdResult = send71(request_deviceId, param71Test10k);
                                    break;
                                case "test10":
                                    byte start = 0;
                                    SaiKe71 param71Test10 = new SaiKe71
                                    {
                                        host = 0,
                                        type = 0,
                                        start = start,
                                        end = (byte)(start + 8)
                                    };
                                    cmdResult = send71(request_deviceId, param71Test10);
                                    for (int i = 0; i < 9; i++)
                                    {
                                        start = (byte)(start + 8);
                                        param71Test10.start = start;
                                        param71Test10.end = (byte)(start + 8);
                                        cmdResult = send71(request_deviceId, param71Test10);
                                    }
                                    break;
                                case "4":
                                    cmdResult = send4(request_deviceId, request_host);
                                    break;
                                case "6":
                                    #region
                                    SaiKe6 param6;
                                    if (string.IsNullOrWhiteSpace(request_param))
                                    {
                                        param6 = new SaiKe6
                                        {
                                            host = request_host,
                                            loop = request_loop
                                        };
                                    }
                                    else
                                    {
                                        param6 = request_param.deserialize<SaiKe6>();
                                    }
                                    cmdResult = send6(request_deviceId, param6);
                                    #endregion
                                    break;
                                case "60":
                                    #region
                                    SaiKe60 param60;
                                    if (string.IsNullOrWhiteSpace(request_param))
                                    {
                                        param60 = new SaiKe60
                                        {
                                            host = request_host,
                                            currentAlarmType = request_loop
                                        };
                                    }
                                    else
                                    {
                                        param60 = request_param.deserialize<SaiKe60>();
                                    }
                                    cmdResult = send60(request_deviceId, param60);
                                    #endregion
                                    break;
                                case "61":
                                    #region
                                    SaiKe61 param61;
                                    if (string.IsNullOrWhiteSpace(request_param))
                                    {
                                        param61 = new SaiKe61
                                        {
                                            host = request_host,
                                            historyAlarmType = request_loop,
                                            start = request_startAddr,
                                            end = request_endAddr
                                        };
                                    }
                                    else
                                    {
                                        param61 = request_param.deserialize<SaiKe61>();
                                    }
                                    cmdResult = send61(request_deviceId, param61);
                                    #endregion
                                    break;
                                case "62":
                                    #region
                                    SaiKe62 param62;
                                    if (string.IsNullOrWhiteSpace(request_param))
                                    {
                                        param62 = new SaiKe62
                                        {
                                            host = request_host,
                                            historyAlarmType = request_loop
                                        };
                                    }
                                    else
                                    {
                                        param62 = request_param.deserialize<SaiKe62>();
                                    }
                                    cmdResult = send62(request_deviceId, param62);
                                    #endregion
                                    break;
                                case "69":
                                    #region
                                    SaiKe69 param69;
                                    if (string.IsNullOrWhiteSpace(request_param))
                                    {
                                        param69 = new SaiKe69
                                        {
                                            host = request_host,
                                            loop = request_loop,
                                            start = request_startAddr,
                                            end = request_endAddr
                                        };
                                    }
                                    else
                                    {
                                        param69 = request_param.deserialize<SaiKe69>();
                                    }
                                    cmdResult = send69(request_deviceId, param69);
                                    #endregion
                                    break;
                                case "70":
                                    #region
                                    SaiKe70 param70;
                                    if (string.IsNullOrWhiteSpace(request_param))
                                    {
                                        param70 = new SaiKe70
                                        {
                                            host = request_host,
                                            loop = request_loop,
                                            start = request_startAddr,
                                            end = request_endAddr
                                        };
                                    }
                                    else
                                    {
                                        param70 = request_param.deserialize<SaiKe70>();
                                    }
                                    cmdResult = send70(request_deviceId, param70);
                                    #endregion
                                    break;
                                case "71":
                                    #region
                                    SaiKe71 param71;
                                    if (string.IsNullOrWhiteSpace(request_param))
                                    {
                                        param71 = new SaiKe71
                                        {
                                            host = request_host,
                                            type = request_loop,
                                            start = request_startAddr,
                                            end = request_endAddr
                                        };
                                    }
                                    else
                                    {
                                        param71 = request_param.deserialize<SaiKe71>();
                                    }
                                    cmdResult = send71(request_deviceId, param71);
                                    #endregion
                                    break;
                                case "72":
                                    #region
                                    SaiKe72 param72;
                                    if (string.IsNullOrWhiteSpace(request_param))
                                    {
                                        param72 = new SaiKe72
                                        {
                                            host = request_host,
                                            type = request_loop
                                        };
                                    }
                                    else
                                    {
                                        param72 = request_param.deserialize<SaiKe72>();
                                    }
                                    cmdResult = send72(request_deviceId, param72);
                                    #endregion
                                    break;
                                case "151":
                                    cmdResult = send151(request_deviceId, request_host);
                                    break;
                                case "152":
                                    cmdResult = send152(request_deviceId, request_host);
                                    break;
                                case "153":
                                    cmdResult = send153(request_deviceId, request_host);
                                    break;
                                case "154_1":
                                    cmdResult = send154(request_deviceId, request_host, 1);
                                    break;
                                case "154_2":
                                    cmdResult = send154(request_deviceId, request_host, 2);
                                    break;
                                case "154_3":
                                    cmdResult = send154(request_deviceId, request_host, 3);
                                    break;
                                case "154_7":
                                    cmdResult = send154(request_deviceId, request_host, 7);
                                    break;
                                case "154_8":
                                    cmdResult = send154(request_deviceId, request_host, 8);
                                    break;
                                case "154_9":
                                    cmdResult = send154(request_deviceId, request_host, 9);
                                    break;
                                default:
                                    break;
                            }
                            result = new AjaxResult(cmdResult, string.Format("deviceId:{0},command:{1}", request_deviceId, request_method));
                        }
                    }
                    showMsg(result.serialize(), "http request");
                }
            }
            catch (Exception ex)
            {
                showMsg(ex.Message, "http message error");
                result = new AjaxResult(false, ex.Message);
                LogHelper.error("http message error", ex);
            }
            byte[] responseBody = Encoding.UTF8.GetBytes(result.serialize());
            httpServer.SendResponse(connectId, HttpStatusCode.Ok, "", null, responseBody, responseBody.Length);
            httpServer.Release(connectId);
            return HttpParseResult.Ok;
        }
        #endregion

        #region Http_OnParseError
        private HttpParseResult Http_OnParseError(IntPtr connectId, int errorCode, string errorDesc)
        {
            showMsg(string.Format("> {0}:{1}", errorCode, errorDesc), "http parse error");
            httpServer.Release(connectId);
            return HttpParseResult.Ok;
        }
        #endregion

        #region Http_OnClose
        private HandleResult Http_OnClose(TcpServer server, IntPtr connectId, SocketOperation enOperation, int errorCode)
        {
            if (errorCode == 0)
                showMsg(string.Format("> {0}:{1},{2}", server.IpAddress, server.Port, connectId), "http close");
            else
                showMsg(string.Format("> {0}:{1},{2} -> op:{3},code:{4}", server.IpAddress, server.Port, connectId, enOperation, errorCode), "http close error");
            if (server.GetExtra(connectId) != null)
            {
                if (server.RemoveExtra(connectId))
                    showMsg(string.Format("> {0}:{1},{2} -> remove extra", server.IpAddress, server.Port, connectId), "http close");
                else
                    showMsg(string.Format("> {0}:{1},{2} -> remove extra fail", server.IpAddress, server.Port, connectId), "http close error");
            }
            return HandleResult.Ok;
        }
        #endregion

        #region Http_OnShutdown
        private HandleResult Http_OnShutdown(TcpServer server)
        {
            showMsg(string.Format("> {0}:{1}", server.IpAddress, server.Port), "http shutdown");
            return HandleResult.Ok;
        }
        #endregion

        #region Tcp_OnAccept
        private HandleResult Tcp_OnAccept(TcpServer server, IntPtr connectId, IntPtr pClient)
        {
            try
            {
                string ip = string.Empty;
                ushort port = 0;
                if (server.GetRemoteAddress(connectId, ref ip, ref port))
                {
                    showMsg(string.Format("> {0}:{1},{2} -> {3}:{4}", server.IpAddress, server.Port, connectId, ip, port), "accept");
                }
                else
                {
                    throw new Exception(string.Format("> {0}:{1},{2} -> get client address error", server.IpAddress, server.Port, connectId));
                }
                TcpClientInfo tcpClientInfo = new TcpClientInfo
                {
                    connectId = connectId,
                    ip = ip,
                    port = port,
                    deviceId = ""
                };
                if (server.SetExtra(connectId, tcpClientInfo) == false)
                {
                    throw new Exception(string.Format("> {0}:{1},{2} -> {3}:{4} set extra fail", server.IpAddress, server.Port, connectId, ip, port));
                }
                //主动查询151
                send151(connectId, ip + port, 0);

                return HandleResult.Ok;
            }
            catch (Exception ex)
            {
                showMsg(ex.Message, "accept error");
                LogHelper.error("accept error", ex);
                return HandleResult.Ignore;
            }
        }
        #endregion

        #region Tcp_OnSend
        private HandleResult Tcp_OnSend(TcpServer server, IntPtr connectId, byte[] bytes)
        {
            try
            {
                showMsg(ByteHelper.bytesToHexString(bytes, "~", 16), "debug send");
                if (server.GetExtra(connectId) is TcpClientInfo tcpClientInfo)
                {
                    showMsg(string.Format("> {0}:{1},{2} -> {3}:{4} ({5} bytes)", server.IpAddress, server.Port, connectId, tcpClientInfo.ip, tcpClientInfo.port, bytes.Length), "send");
                }
                else
                {
                    showMsg(string.Format("> {0}:{1},{2} -> ({3} bytes)", server.IpAddress, server.Port, connectId, bytes.Length), "send");
                }
                return HandleResult.Ok;
            }
            catch (Exception ex)
            {
                showMsg(ex.Message, "send error");
                LogHelper.error("send error", ex);
                return HandleResult.Ignore;
            }
        }
        #endregion

        #region Tcp_OnReceive
        private HandleResult Tcp_OnReceive(TcpServer server, IntPtr connectId, byte[] bytes)
        {
            try
            {
                showMsg(ByteHelper.bytesToHexString(bytes, "^", 16), "debug receive");
                if (bytes.Length < 7) return HandleResult.Ok;
                if (server.GetExtra(connectId) is TcpClientInfo tcpClientInfo)
                {
                    showMsg(string.Format("> {0}:{1},{2} -> {3}:{4} ({5} bytes)", server.IpAddress, server.Port, connectId, tcpClientInfo.ip, tcpClientInfo.port, bytes.Length), "receive");
                    byte versionSerialNo = bytes[SaiKeUtility.VerNoBytePos];
                    byte commandCode = bytes[SaiKeUtility.CmdBytePos];
                    SaiKeCmd saikeCmd = new SaiKeCmd
                    {
                        deviceId = tcpClientInfo.deviceId,
                        command = commandCode,
                        eventTime = DateTime.Now,
                        rawData = ByteHelper.bytesToHexString(bytes)
                    };
                    switch (commandCode)
                    {
                        case 4:
                            #region
                            if (deviceFrame.ContainsKey(saikeCmd.deviceId))
                            {
                                byte[] frame = deviceFrame[saikeCmd.deviceId];
                                if (frame[SaiKeUtility.VerNoBytePos] == versionSerialNo && frame[SaiKeUtility.CmdBytePos] == commandCode)
                                {
                                    SaiKe4 saike4 = new SaiKe4(bytes)
                                    {
                                        host = frame[SaiKeUtility.BodyBytePos]
                                    };
                                    //ack
                                    SaiKeUtility.sendToApp(txtAppUrl.Text, saikeCmd.serialize(), saike4.serialize());
                                }
                            }
                            #endregion
                            break;
                        case 6:
                            #region
                            if (deviceFrame.ContainsKey(saikeCmd.deviceId))
                            {
                                byte[] frame = deviceFrame[saikeCmd.deviceId];
                                if (frame[SaiKeUtility.VerNoBytePos] == versionSerialNo && frame[SaiKeUtility.CmdBytePos] == commandCode)
                                {
                                    SaiKe6 saike6 = new SaiKe6(bytes)
                                    {
                                        host = frame[SaiKeUtility.BodyBytePos],
                                        loop = frame[SaiKeUtility.BodyBytePos + 1]
                                    };
                                    //ack
                                    SaiKeUtility.sendToApp(txtAppUrl.Text, saikeCmd.serialize(), saike6.serialize());
                                }
                            }
                            #endregion
                            break;
                        case 61:
                            #region
                            if (deviceFrame.ContainsKey(saikeCmd.deviceId))
                            {
                                byte[] frame = deviceFrame[saikeCmd.deviceId];
                                if (frame[SaiKeUtility.VerNoBytePos] == versionSerialNo && frame[SaiKeUtility.CmdBytePos] == commandCode)
                                {
                                    SaiKe61 saike61 = new SaiKe61(bytes)
                                    {
                                        host = frame[SaiKeUtility.BodyBytePos],
                                        historyAlarmType = frame[SaiKeUtility.BodyBytePos + 1],
                                        start = (ushort)((frame[SaiKeUtility.BodyBytePos + 3] << 8) | frame[SaiKeUtility.BodyBytePos + 2]),
                                        end = (ushort)((frame[SaiKeUtility.BodyBytePos + 5] << 8) | frame[SaiKeUtility.BodyBytePos + 4])
                                    };
                                    //showMsg(saike61.serialize(), "debug saike61");
                                    //ack
                                    SaiKeUtility.sendToApp(txtAppUrl.Text, saikeCmd.serialize(), saike61.serialize());
                                }
                            }
                            #endregion
                            break;
                        case 62:
                            #region
                            if (deviceFrame.ContainsKey(saikeCmd.deviceId))
                            {
                                byte[] frame = deviceFrame[saikeCmd.deviceId];
                                if (frame[SaiKeUtility.VerNoBytePos] == versionSerialNo && frame[SaiKeUtility.CmdBytePos] == commandCode)
                                {
                                    bool dataIsGood = bytes.Length > SaiKeUtility.BodyBytePos + 3;
                                    SaiKe62 saike62 = new SaiKe62()
                                    {
                                        host = frame[SaiKeUtility.BodyBytePos],
                                        historyAlarmType = dataIsGood ? bytes[SaiKeUtility.BodyBytePos] : (byte)0,
                                        count = dataIsGood ? (ushort)((bytes[SaiKeUtility.BodyBytePos + 2] << 8) | bytes[SaiKeUtility.BodyBytePos + 1]) : (ushort)0,
                                        historyAlarmTypeText = EnumDescription.getDescription<HistoryAlarmType>(dataIsGood ? bytes[SaiKeUtility.BodyBytePos] : 0)
                                    };
                                    //showMsg(saike62.serialize(), "debug saike62");
                                    //ack
                                    SaiKeUtility.sendToApp(txtAppUrl.Text, saikeCmd.serialize(), saike62.serialize());
                                }
                            }
                            #endregion
                            break;
                        case 69:
                            #region
                            if (deviceFrame.ContainsKey(saikeCmd.deviceId))
                            {
                                byte[] frame = deviceFrame[saikeCmd.deviceId];
                                if (frame[SaiKeUtility.VerNoBytePos] == versionSerialNo && frame[SaiKeUtility.CmdBytePos] == commandCode)
                                {
                                    SaiKe69 saike69 = new SaiKe69
                                    {
                                        host = frame[SaiKeUtility.BodyBytePos],
                                        loop = frame[SaiKeUtility.BodyBytePos + 1],
                                        start = frame[SaiKeUtility.BodyBytePos + 2],
                                        end = frame[SaiKeUtility.BodyBytePos + 3]
                                    };
                                    //ack
                                    SaiKeUtility.sendToApp(txtAppUrl.Text, saikeCmd.serialize(), saike69.serialize());
                                }
                            }
                            #endregion
                            break;
                        case 110:
                            #region
                            SaiKe110 saike110 = new SaiKe110(bytes);
                            ack(connectId, saikeCmd.deviceId, versionSerialNo, saikeCmd.command);
                            SaiKeUtility.sendToApp(txtAppUrl.Text, saikeCmd.serialize(), saike110.serialize());
                            #endregion
                            break;
                        case 151:
                            #region
                            SaiKe151 saike151 = new SaiKe151(bytes);
                            if (tcpClientInfo.deviceId != saike151.firmwareId)
                            {
                                tcpClientInfo.deviceId = saike151.firmwareId;
                                saikeCmd.deviceId = saike151.firmwareId;
                            }
                            bool needAck = true;
                            if (deviceFrame.ContainsKey(saikeCmd.deviceId))
                            {
                                byte[] frame = deviceFrame[saikeCmd.deviceId];
                                if (frame[SaiKeUtility.VerNoBytePos] == versionSerialNo && frame[SaiKeUtility.CmdBytePos] == commandCode)
                                {
                                    needAck = false;
                                }
                            }
                            if (deviceFrame.ContainsKey(tcpClientInfo.ip + tcpClientInfo.port))
                            {
                                byte[] frame = deviceFrame[tcpClientInfo.ip + tcpClientInfo.port];
                                if (frame[SaiKeUtility.VerNoBytePos] == versionSerialNo && frame[SaiKeUtility.CmdBytePos] == commandCode)
                                {
                                    needAck = false;
                                }
                            }
                            if (needAck)
                                ack(connectId, saikeCmd.deviceId, versionSerialNo, saikeCmd.command);
                            SaiKeUtility.sendToApp(txtAppUrl.Text, saikeCmd.serialize(), saike151.serialize());
                            #endregion
                            break;
                        case 152:
                            #region
                            SaiKe152 saike152 = new SaiKe152(bytes);
                            //ack
                            SaiKeUtility.sendToApp(txtAppUrl.Text, saikeCmd.serialize(), saike152.serialize());
                            #endregion
                            break;
                        case 250:
                            #region
                            SaiKe250 saike250 = new SaiKe250(bytes);
                            //ack
                            SaiKeUtility.sendToApp(txtAppUrl.Text, saikeCmd.serialize(), saike250.serialize());
                            #endregion
                            break;
                        default:
                            break;
                    }
                    showMsg(string.Format("deviceId:{0},command:{1}", saikeCmd.deviceId, saikeCmd.command), "data");
                    if (string.IsNullOrWhiteSpace(tcpClientInfo.deviceId))
                    {
                        //主动查询151
                        send151(connectId, tcpClientInfo.ip + tcpClientInfo.port, 0);
                    }
                }
                else
                {
                    throw new Exception(string.Format("> {0}:{1},{2} -> null ({3} bytes)", server.IpAddress, server.Port, connectId, bytes.Length));
                }
                return HandleResult.Ok;
            }
            catch (Exception ex)
            {
                showMsg(ex.Message, "receive error");
                LogHelper.error("receive error", ex);
                return HandleResult.Ignore;
            }
        }
        #endregion

        #region Tcp_OnClose
        private HandleResult Tcp_OnClose(TcpServer server, IntPtr connectId, SocketOperation enOperation, int errorCode)
        {
            if (errorCode == 0)
                showMsg(string.Format("> {0}:{1},{2}", server.IpAddress, server.Port, connectId), "tcp close");
            else
                showMsg(string.Format("> {0}:{1},{2} -> op:{3},code:{4}", server.IpAddress, server.Port, connectId, enOperation, errorCode), "tcp close error");
            if (server.GetExtra(connectId) is TcpClientInfo tcpClientInfo)
            {
                SaiKeCmd saikeCmd = new SaiKeCmd
                {
                    deviceId = tcpClientInfo.deviceId,
                    command = 0xFF,
                    eventTime = DateTime.Now,
                    rawData = "",
                };
                SaiKeUtility.sendToApp(txtAppUrl.Text, saikeCmd.serialize(), "");
                if (deviceFrame.ContainsKey(tcpClientInfo.deviceId))
                {
                    deviceFrame.Remove(tcpClientInfo.deviceId);
                }
                if (deviceFrame.ContainsKey(tcpClientInfo.ip + tcpClientInfo.port))
                {
                    deviceFrame.Remove(tcpClientInfo.ip + tcpClientInfo.port);
                }
                if (server.RemoveExtra(connectId))
                    showMsg(string.Format("> {0}:{1},{2} -> remove extra", server.IpAddress, server.Port, connectId), "tcp close");
                else
                    showMsg(string.Format("> {0}:{1},{2} -> remove extra fail", server.IpAddress, server.Port, connectId), "tcp close error");
            }
            return HandleResult.Ok;
        }
        #endregion

        #region Tcp_OnShutdown
        private HandleResult Tcp_OnShutdown(TcpServer server)
        {
            showMsg(string.Format("> {0}:{1}", server.IpAddress, server.Port), "tcp shutdown");
            return HandleResult.Ok;
        }
        #endregion

        #region btnStartHttp_Click
        private void btnStartHttp_Click(object sender, EventArgs e)
        {
            try
            {
                ushort.TryParse(txtHttpPort.Text, out ushort httpPort);
                if (httpPort < 1000) return;
                httpServer = new HttpServer
                {
                    IpAddress = "0.0.0.0",
                    Port = httpPort
                };
                //httpServer.OnHeadersComplete += new HttpEvent.OnHeadersCompleteEventHandler(Http_OnHeadersComplete);
                httpServer.OnBody += new HttpEvent.OnBodyEventHandler(Http_OnBody);
                httpServer.OnMessageComplete += new HttpEvent.OnMessageCompleteEventHandler(Http_OnMessageComplete);
                httpServer.OnParseError += new HttpEvent.OnParseErrorEventHandler(Http_OnParseError);
                httpServer.OnClose += new TcpServerEvent.OnCloseEventHandler(Http_OnClose);
                httpServer.OnShutdown += new TcpServerEvent.OnShutdownEventHandler(Http_OnShutdown);
                if (httpServer.Start())
                {
                    setButton(tcpIsOpen, true);
                    showMsg(string.Format("> {0}:{1}", httpServer.IpAddress, httpServer.Port), "http start");
                }
                else
                {
                    showMsg(string.Format("> {0}:{1} start error {2}:{3}", httpServer.IpAddress, httpServer.Port, httpServer.ErrorCode, httpServer.ErrorMessage), "http start");
                }

            }
            catch (Exception ex)
            {
                showMsg(ex.Message, "http start error");
                LogHelper.error("http start error", ex);
            }
        }
        #endregion

        #region btnStopHttp_Click
        private void btnStopHttp_Click(object sender, EventArgs e)
        {
            try
            {
                if (httpServer.Stop())
                {
                    setButton(tcpIsOpen, false);
                    showMsg(string.Format("> {0}:{1}", httpServer.IpAddress, httpServer.Port), "http stop");
                }
                else
                {
                    showMsg(string.Format("> {0}:{1} stop error {2}:{3}", httpServer.IpAddress, httpServer.Port, httpServer.ErrorCode, httpServer.ErrorMessage), "http stop");
                }
            }
            catch (Exception ex)
            {
                showMsg(ex.Message, "http stop error");
                LogHelper.error("http stop error", ex);
            }
        }
        #endregion

        #region btnStartTcp_Click
        private void btnStartTcp_Click(object sender, EventArgs e)
        {
            try
            {
                ushort.TryParse(txtTcpPort.Text, out ushort tcpPort);
                if (tcpPort < 1000) return;
                tcpServer = new TcpServer
                {
                    SocketBufferSize = 1348,
                    KeepAliveTime = 1000,
                    KeepAliveInterval = 1000,
                    IpAddress = "0.0.0.0",
                    Port = tcpPort
                };
                //server.OnPrepareListen += new TcpServerEvent.OnPrepareListenEventHandler(OnPrepareListen);
                tcpServer.OnAccept += new TcpServerEvent.OnAcceptEventHandler(Tcp_OnAccept);
                tcpServer.OnSend += new TcpServerEvent.OnSendEventHandler(Tcp_OnSend);
                ////server.OnPointerDataReceive += new TcpServerEvent.OnPointerDataReceiveEventHandler(OnPointerDataReceive);
                tcpServer.OnReceive += new TcpServerEvent.OnReceiveEventHandler(Tcp_OnReceive);
                tcpServer.OnClose += new TcpServerEvent.OnCloseEventHandler(Tcp_OnClose);
                tcpServer.OnShutdown += new TcpServerEvent.OnShutdownEventHandler(Tcp_OnShutdown);
                if (tcpServer.Start())
                {
                    setButton(true, httpIsOpen);
                    showMsg(string.Format("> {0}:{1}", tcpServer.IpAddress, tcpServer.Port), "tcp start");
                }
                else
                {
                    showMsg(string.Format("> {0}:{1} start error {2}:{3}", tcpServer.IpAddress, tcpServer.Port, tcpServer.ErrorCode, tcpServer.ErrorMessage), "tcp start");
                }
            }
            catch (Exception ex)
            {
                showMsg(ex.Message, "tcp start error");
                LogHelper.error("tcp start error", ex);
            }
        }
        #endregion

        #region btnStopTcp_Click
        private void btnStopTcp_Click(object sender, EventArgs e)
        {
            try
            {
                if (tcpServer.Stop())
                {
                    setButton(false, httpIsOpen);
                    showMsg(string.Format("> {0}:{1}", tcpServer.IpAddress, tcpServer.Port), "tcp stop");
                }
                else
                {
                    showMsg(string.Format("> {0}:{1} stop error {2}:{3}", tcpServer.IpAddress, tcpServer.Port, tcpServer.ErrorCode, tcpServer.ErrorMessage), "tcp stop");
                }
            }
            catch (Exception ex)
            {
                showMsg(ex.Message, "tcp stop error");
                LogHelper.error("tcp stop error", ex);
            }
        }
        #endregion

        #region ack
        private void ack(IntPtr connectId, string deviceId, byte ver_no, byte cmd)
        {
            byte[] ackFrame = new byte[]
            {
                0x53,0x4E,
                0x00,0x00,  //长度
                0x10,       //版本流水号
                0xFA,       //命令250
                0x01,       //0-错误 1-正确 2-不合法
                cmd,        //原始命令
                0x00,
                0x00,
                0x00        //crc
            };
            send(connectId, deviceId, ackFrame, ver_no);
        }
        #endregion

        #region send4
        private bool send4(string deviceId, byte host)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                byte[] frame4 = new byte[]
                {
                    0x53,0x4E,
                    0x00,0x00,  //长度
                    0x10,       //版本流水号
                    0x04,       //命令4
                    host,       //主机
                    0x00,       //下标6
                    0x00,       //下标7
                    0x00,       //下标8
                    0x00        //crc
                };
                result = send(deviceId, frame4);
            }
            return result;
        }
        #endregion

        #region send6
        private bool send6(string deviceId, SaiKe6 param6)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                byte[] frame4 = new byte[]
                {
                    0x53,0x4E,
                    0x00,0x00,      //长度
                    0x10,           //版本流水号
                    0x06,           //命令6
                    param6.host,    //主机
                    param6.loop,    //回路
                    0x00,           //下标7
                    0x00,           //下标8
                    0x00             //crc
                };
                result = send(deviceId, frame4);
            }
            return result;
        }
        #endregion

        #region send60
        private bool send60(string deviceId, SaiKe60 param60)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                byte[] frame60 = new byte[]
                {
                    0x53,0x4E,
                    0x00,0x00,                  //长度
                    0x10,                       //版本流水号
                    0x3C,                       //命令60
                    param60.host,               //主机
                    param60.currentAlarmType,   //当前报警类型
                    0x00,                       //下标7
                    0x00,                       //下标8
                    0x00                        //crc
                };
                result = send(deviceId, frame60);
            }
            return result;
        }
        #endregion

        #region send61
        private bool send61(string deviceId, SaiKe61 param61)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                byte[] frame61 = new byte[]
                {
                    0x53,0x4E,
                    0x00,0x00,                              //长度
                    0x10,                                   //版本流水号
                    0x3D,                                   //命令61
                    param61.host,                           //主机
                    param61.historyAlarmType,               //历史报警类型
                    (byte)(param61.start & 0xFF),           //开始地址(低)
                    (byte)((param61.start & 0xFF00) >> 8),  //开始地址(高)
                    (byte)(param61.end & 0xFF),             //结束地址(低)
                    (byte)((param61.end & 0xFF00) >> 8),    //结束地址(高)
                    0x00                                    //crc
                };
                result = send(deviceId, frame61);
            }
            return result;
        }
        #endregion

        #region send62
        private bool send62(string deviceId, SaiKe62 param62)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                byte[] frame62 = new byte[]
                {
                    0x53,0x4E,
                    0x00,0x00,                  //长度
                    0x10,                       //版本流水号
                    0x3E,                       //命令62
                    param62.host,               //主机
                    param62.historyAlarmType,   //历史报警类型
                    0x00,                       //下标7
                    0x00,                       //下标8
                    0x00                        //crc
                };
                result = send(deviceId, frame62);
            }
            return result;
        }
        #endregion

        #region send69
        private bool send69(string deviceId, SaiKe69 param69)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                byte[] frame69 = new byte[]
                {
                    0x53,0x4E,
                    0x00,0x00,      //长度
                    0x10,           //版本流水号
                    0x45,           //命令69
                    param69.host,   //主机
                    param69.loop,   //回路
                    param69.start,  //开始地址
                    param69.end,    //结束地址
                    0x00,           //下标9
                    0x00            //crc
                };
                result = send(deviceId, frame69);
            }
            return result;
        }
        #endregion

        #region send70
        private bool send70(string deviceId, SaiKe70 param70)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                int dataLength = (param70.end - param70.start) * 32;
                if (dataLength > 0)
                {
                    if (param70.data.Length > dataLength * 2)
                        param70.data = param70.data.Substring(0, dataLength * 2);
                    else if (param70.data.Length < dataLength * 2)
                        param70.data = param70.data.PadRight(dataLength * 2, 'F');
                    byte[] frame70 = new byte[dataLength + 7 + 4];
                    frame70[0] = 0x53;
                    frame70[1] = 0x4E;
                    frame70[2] = 0x00;                              //长度
                    frame70[3] = 0x00;
                    frame70[4] = 0x10;                              //版本流水号 VerNoBytePos
                    frame70[5] = 0x46;                              //命令70 CmdBytePos
                    frame70[6] = param70.host;                      //主机 BodyBytePos
                    frame70[7] = param70.loop;                      //回路
                    frame70[8] = param70.start;                     //开始地址
                    frame70[9] = param70.end;                       //结束地址
                    Buffer.BlockCopy(ByteHelper.hexStringToBytes(param70.data), 0, frame70, 10, dataLength);
                    result = send(deviceId, frame70);
                }
            }
            return result;
        }
        #endregion

        #region send71
        private bool send71(string deviceId, SaiKe71 param71)
        {
            bool result = false;
            bool test = param71.data.Length == 0;
            if (!string.IsNullOrEmpty(deviceId))
            {
                int dataLength = (param71.end - param71.start) * 128;
                if (dataLength > 0)
                {
                    //Random rd = new Random();
                    //Char.TryParse(rd.Next(16).ToString("x"), out char rdChar);
                    if (param71.data.Length > dataLength * 2)
                        param71.data = param71.data.Substring(0, dataLength * 2);
                    else if (param71.data.Length < dataLength * 2)
                        param71.data = param71.data.PadRight(dataLength * 2, 'F');
                    byte[] frame71 = new byte[dataLength + 7 + 6];
                    frame71[0] = 0x53;
                    frame71[1] = 0x4E;
                    frame71[2] = 0x00;                                      //长度
                    frame71[3] = 0x00;
                    frame71[4] = 0x10;                                      //版本流水号 VerNoBytePos
                    frame71[5] = 0x47;                                      //命令71 CmdBytePos
                    frame71[6] = param71.host;                              //主机 BodyBytePos
                    frame71[7] = param71.type;                              //类型
                    frame71[8] = (byte)(param71.start & 0xFF);              //开始地址(低)
                    frame71[9] = (byte)((param71.start & 0xFF00) >> 8);     //开始地址(高)
                    frame71[10] = (byte)(param71.end & 0xFF);               //结束地址(低)
                    frame71[11] = (byte)((param71.end & 0xFF00) >> 8);      //结束地址(高)
                    if (test)
                    {
                        for (int i = 12; i < frame71.Length; i++)
                        {
                            frame71[i] = (byte)i;
                        }
                    }
                    else
                    {
                        Buffer.BlockCopy(ByteHelper.hexStringToBytes(param71.data), 0, frame71, 12, dataLength);
                    }
                    result = send(deviceId, frame71);
                }
            }
            return result;
        }
        #endregion

        #region send72
        private bool send72(string deviceId, SaiKe72 param72)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                byte[] frame72 = new byte[]
                {
                    0x53,0x4E,
                    0x00,0x00,      //长度
                    0x10,           //版本流水号
                    0x48,           //命令72
                    param72.host,   //主机
                    param72.type,   //类型
                    0x00,           //下标7
                    0x00,           //下标8
                    0x00            //crc
                };
                result = send(deviceId, frame72);
            }
            return result;
        }
        #endregion

        #region send151
        private bool send151(string deviceId, byte host)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                byte[] frame151 = new byte[]
                {
                    0x53,0x4E,
                    0x00,0x00,  //长度
                    0x10,   //版本流水号
                    0x97,   //命令151
                    host,   //主机
                    0x00,   //下标6
                    0x00,   //下标7
                    0x00,   //下标8
                    0x00    //crc
                };
                result = send(deviceId, frame151);
            }
            return result;
        }
        #endregion

        #region send151
        private bool send151(IntPtr connectId, string deviceId, byte host)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                byte[] frame151 = new byte[]
                {
                    0x53,0x4E,
                    0x00,0x00,  //长度
                    0x10,   //版本流水号
                    0x97,   //命令151
                    host,   //主机
                    0x00,   //下标6
                    0x00,   //下标7
                    0x00,   //下标8
                    0x00    //crc
                };
                result = send(connectId, deviceId, frame151);
            }
            return result;
        }
        #endregion

        #region send152
        private bool send152(string deviceId, byte host)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                byte[] frame152 = new byte[]
                {
                    0x53,0x4E,
                    0x00,0x00,  //长度
                    0x10,       //版本流水号
                    0x98,       //命令152
                    host,       //主机
                    0x00,       //下标6
                    0x00,       //下标7
                    0x00,       //下标8
                    0x00        //crc
                };
                result = send(deviceId, frame152);
            }
            return result;
        }
        #endregion

        #region send153
        private bool send153(string deviceId, byte host)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                byte[] frame153 = new byte[]
                {
                    0x53,0x4E,
                    0x00,0x00,  //长度
                    0x10,       //版本流水号
                    0x99,       //命令153
                    host,       //主机
                    0x00,       //下标6
                    0x00,       //下标7
                    0x00,       //下标8
                    0x00        //crc
                };
                result = send(deviceId, frame153);
            }
            return result;
        }
        #endregion

        #region send154
        private bool send154(string deviceId, byte host, byte type)
        {
            bool result = false;
            if (!string.IsNullOrEmpty(deviceId))
            {
                byte[] frame154 = new byte[]
                {
                    0x53,0x4E,
                    0x00,0x00,  //长度
                    0x10,       //版本流水号
                    0x9A,       //命令154
                    type,       //1-消音 2-复位 3-自检 7-擦除Flash 8-锁机 9-解锁
                    host,       //主机
                    0x00,       //下标7
                    0x00,       //下标8
                    0x00,       //下标9
                    0x00        //crc
                };
                result = send(deviceId, frame154);
            }
            return result;
        }
        #endregion

        #region send
        private bool send(string deviceId, byte[] frame)
        {
            IntPtr connectId = IntPtr.Zero;
            IntPtr[] allConnectId = tcpServer.GetAllConnectionIDs();
            if (allConnectId != null)
            {
                foreach (IntPtr connid in allConnectId)
                {
                    if (tcpServer.GetExtra(connid) is TcpClientInfo tcpClientInfo)
                    {
                        if (tcpClientInfo.deviceId == deviceId)
                        {
                            connectId = connid;
                            break;
                        }
                    }
                }
            }
            if (connectId == IntPtr.Zero)
                return false;
            else
                return send(connectId, deviceId, frame);
        }
        private bool send(IntPtr connectId, string deviceId, byte[] frame, byte ver_no = 0xFF)
        {
            if (frame.Length < 7) return false;

            //重置长度、流水号
            frame[SaiKeUtility.LengthBytePos] = (byte)(frame.Length & 0xFF);
            frame[SaiKeUtility.LengthBytePos + 1] = (byte)((frame.Length >> 8) & 0xFF);
            frame[SaiKeUtility.VerNoBytePos] = ver_no == 0xFF ? getNextSerialNo(deviceId) : ver_no;

            byte crc = 0;
            for (int i = 0; i < frame.Length - 1; i++)
            {
                crc += frame[i];
            }
            frame[frame.Length - 1] = crc;

            if (frame[SaiKeUtility.CmdBytePos] != 0xFA) deviceFrame[deviceId] = frame;

            //int offset = 0;
            //int size = frame.Length;
            //bool result = true;
            //Stopwatch stopwatch = new Stopwatch();
            //stopwatch.Start();
            //while (result && size > 1348)
            //{
            //    //showMsg(string.Format("send {0} bytes", size), "debug send");
            //    result = tcpServer.Send(connectId, frame, offset, 1348);
            //    offset += 1348;
            //    size -= 1348;
            //    tinyWait(200, stopwatch);
            //}
            //stopwatch.Stop();
            //if (result && size > 0)
            //{
            //    //showMsg(string.Format("send {0} bytes", size), "debug send");
            //    result = tcpServer.Send(connectId, frame, offset, size);
            //}
            //return result;

            return tcpServer.Send(connectId, frame, frame.Length);
        }
        #endregion

        #region getLastSerialNo
        private byte getLastSerialNo(string deviceId)
        {
            if (!deviceVerNo.ContainsKey(deviceId)) deviceVerNo[deviceId] = 0xFF;
            return deviceVerNo[deviceId];
        }
        #endregion

        #region getNextSerialNo
        private byte getNextSerialNo(string deviceId)
        {
            deviceVerNo[deviceId] = (byte)((getLastSerialNo(deviceId) + 1) & 0xF | 0x10);
            return deviceVerNo[deviceId];
        }
        #endregion

        #region tinyWait
        private void tinyWait(long duration)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            tinyWait(duration, stopwatch);
            stopwatch.Stop();
        }
        private void tinyWait(long duration, Stopwatch stopwatch)
        {
            long current = stopwatch.ElapsedMilliseconds;
            while ((stopwatch.ElapsedMilliseconds - current) < duration)
            {
                Thread.Sleep(1);
            }
        }
        #endregion
    }
}
